package com.xjrh.traintravel.collector.controller;

import com.xjrh.common.core.utils.ObjectUtils;
import com.xjrh.common.core.web.AjaxResult;
import com.xjrh.common.core.web.BaseController;
import com.xjrh.middleware.redis.service.RedisService;
import com.xjrh.traintravel.collector.common.CRC16M;
import com.xjrh.traintravel.collector.common.CodeConvertor;
import com.xjrh.traintravel.collector.common.ResultData;
import com.xjrh.traintravel.collector.common.StringUtil;
import com.xjrh.traintravel.collector.controller.param.MvbDataCommandParam;
import com.xjrh.traintravel.collector.netty.coder.command.AgreementCommandEnum;
import com.xjrh.traintravel.collector.netty.command.upgrade.UpgradeEquipmentData;
import com.xjrh.traintravel.collector.netty.command.upgrade.UpgradeProgress;
import com.xjrh.traintravel.collector.netty.handler.*;
import com.xjrh.traintravel.collector.netty.coder.command.AgreementStructure;
import com.xjrh.traintravel.collector.pojo.config.*;
import com.xjrh.traintravel.collector.runner.DataCacheTool;
import com.xjrh.traintravel.collector.runner.NettyClientManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 通讯命令管理Controller
 *  <p>
 *      SERVER主动下发命令的操作入口
 *  </p>
 * @author snps
 * @create 2022-01-21 13:38
 * @describes
 **/
@RequestMapping("/agreement/manage")
@RestController
@Slf4j
public class AgreementManageController extends BaseController {

    /**
     * 数据缓存时间（单位：分钟）
     */
    @Value("${project.dataCacheTime}")
    private long dataCacheTime;

    @Resource
    private ConfigHandler configHandler;

    @Resource
    private ConfigReadHandler configReadHandler;

    @Resource
    private FrontHandler frontHandler;

    @Resource
    private CheckHandler checkHandler;

    @Resource
    private UpgradeHandler upgradeHandler;

    @Resource
    private MvbHandler mvbHandler;

    @Resource
    private FileHandler fileHandler;

    @Resource
    private DataCacheTool dataCacheTool;

    @Resource
    private RedisService redisService;

    @Resource
    private NettyClientManager nettyClientManager;


    /************************************************************
     * 下发 配置-主机信息 (01)
     * @param configVo 设置参数-车辆及服务器信息
     */
    @PostMapping("/configInfo")
    public AjaxResult configInfo(@RequestBody InfoConfig configVo) {
        // 检查参数
        if (StringUtil.isEmpty(configVo.getEquipmentMacAddr())) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = configHandler.buildCommand(configVo.getCommonNo(), configVo);
        configHandler.sendConfigCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 配置-采集策略 (02)
     * @param configVo 设置参数-采集策略
     */
    @PostMapping("/configCollectionStrategy")
    public AjaxResult configCollectionStrategy(@RequestBody CollectionStrategyConfig configVo) {
        // 检查参数
        if (StringUtil.isEmpty(configVo.getEquipmentMacAddr())) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = configHandler.buildCommand(configVo.getCommonNo(), configVo);
        configHandler.sendConfigCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 配置-故障分级 (03)
     * @param configVo 设置参数-故障分级
     */
    @PostMapping("/configFaultGrade")
    public AjaxResult configFaultGrade(@RequestBody FaultGradeConfig configVo) {
        // 检查参数
        if (StringUtil.isEmpty(configVo.getEquipmentMacAddr())) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = configHandler.buildCommand(configVo.getCommonNo(), configVo);
        configHandler.sendConfigCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 设置-系统时间 (04)
     * @param configVo 设置-系统时间
     */
    @PostMapping("/configSystemTime")
    public AjaxResult configSystemTime(@RequestBody SystemTimeConfig configVo) {
        // 检查参数
        if (StringUtil.isEmpty(configVo.getEquipmentMacAddr())) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = configHandler.buildCommand(configVo.getCommonNo(), configVo);
        configHandler.sendConfigCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 设置参数-波磨设置 (05)
     * @param configVo 设置参数-波磨设置
     */
    @PostMapping("/configCorrugation")
    public AjaxResult configCorrugation(@RequestBody CorrugationConfig configVo) {
        // 检查参数
        if (StringUtil.isEmpty(configVo.getEquipmentMacAddr())) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = configHandler.buildCommand(configVo.getCommonNo(), configVo);
        configHandler.sendConfigCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 设置参数-心跳设置 (06)
     * @param configVo 设置参数-心跳设置
     */
    @PostMapping("/configHeartbeat")
    public AjaxResult configHeartbeat(@RequestBody HeartbeatConfig configVo) {
        // 检查参数
        if (StringUtil.isEmpty(configVo.getEquipmentMacAddr())) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = configHandler.buildCommand(configVo.getCommonNo(), configVo);
        configHandler.sendConfigCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 配置-前置采集重置（重新采集前置信息）
     */
    @PostMapping("/config0A")
    public AjaxResult config0A(@RequestBody Config0A configVo) {
        // 检查参数
        if (StringUtil.isEmpty(configVo.getEquipmentMacAddr())) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = configHandler.buildCommand(configVo.getCommonNo(), configVo);
        configHandler.sendConfigCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 配置-上传文件类型控制
     */
    @PostMapping("/config0B")
    public AjaxResult config0B(@RequestBody Config0B configVo) {
        // 检查参数
        if (StringUtil.isEmpty(configVo.getEquipmentMacAddr())) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = configHandler.buildCommand(configVo.getCommonNo(), configVo);
        configHandler.sendConfigCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 配置-设备ID
     */
    @PostMapping("/config0C")
    public AjaxResult config0C(@RequestBody Config0C configVo) {
        // 检查参数
        if (StringUtil.isEmpty(configVo.getEquipmentMacAddr())) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = configHandler.buildCommand(configVo.getCommonNo(), configVo);
        configHandler.sendConfigCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 配置-删除存储设置
     */
    @PostMapping("/config0D")
    public AjaxResult config0D(@RequestBody Config0D configVo) {
        // 检查参数
        if (StringUtil.isEmpty(configVo.getEquipmentMacAddr())) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = configHandler.buildCommand(configVo.getCommonNo(), configVo);
        configHandler.sendConfigCommand(agreement);
        return success("发送指令成功!");
    }


    /************************************************************
     * 下发 读取配置 (03)
     * @param equipmentId 设备标识
     * @param commandNo 要读取的配置参数命令号
     * @param subCommandNo 要读取的“子级”配置编号（目前只有“故障分级“适用）
     */
    @PostMapping("/readConfig")
    public AjaxResult readConfig(String equipmentId, String commandNo,
                                 @RequestParam(value = "subCommandNo", required = false) String subCommandNo) {
        // 检查参数
        if (StringUtil.isEmpty(equipmentId)) {
            log.error("设备标识 为空，不能下发指令!");
            return error("设备标识为空，不能下发配置指令!");
        }
        if (StringUtil.isEmpty(commandNo)) {
            log.error("要读取的配置参数命令号 为空，不能下发指令!");
            return error("要读取的配置参数命令号 为空，不能下发指令!");
        }

        try {
            // 构造协议内容
            String data = commandNo;
            if (ObjectUtils.isNotEmpty(subCommandNo)) {
                data = commandNo + subCommandNo;
            }
            AgreementStructure agreement = new AgreementStructure(AgreementCommandEnum.S_03.getValue(), equipmentId, "01");
            // 序号(1)
            agreement.setSeq(this.getEquipmentSeq(agreement.getAddr()));
            // 长度(2)
            agreement.setLen(CodeConvertor.intToHexString(data.length()/2, 2));
            // 数据(N)
            agreement.setData(data);
            // 校验(2)
            String crcValue = CRC16M.calculateCrc(agreement);
            agreement.setFcs(crcValue);

            // 发送命令
            configReadHandler.sendReadCommand(agreement);
            return success("发送指令成功!");
        } catch (Exception e) {
            e.printStackTrace();
            return error("发送指令失败!");
        }
    }


    /***************************************************************************
     * 下发 前置版本信息获取 (06-01)
     * @param equipmentId 设备标识
     */
    @PostMapping("/getFrontInfo")
    public AjaxResult getFrontInfo(String equipmentId) {
        // 检查参数
        ResultData result = new ResultData(Boolean.FALSE);
        if (StringUtil.isEmpty(equipmentId)) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令
        AgreementStructure agreement = frontHandler.buildCommand(equipmentId, "01");
        // 发送命令
        frontHandler.sendGetInfoCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 DSP版本信息获取 (06-02)
     * @param equipmentId 设备标识
     */
    @PostMapping("/getDspInfo")
    public AjaxResult getDspInfo(String equipmentId) {
        // 检查参数
        ResultData result = new ResultData(Boolean.FALSE);
        if (StringUtil.isEmpty(equipmentId)) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令
        AgreementStructure agreement = frontHandler.buildCommand(equipmentId, "02");
        // 发送命令
        frontHandler.sendGetInfoCommand(agreement);
        return success("发送指令成功!");
    }


    /**
     * 下发 主机（软硬件）版本信息获取 (06-03)
     * @param equipmentId 设备标识
     */
    @PostMapping("/getVersionInfo")
    public AjaxResult getVersionInfo(String equipmentId) {
        // 检查参数
        ResultData result = new ResultData(Boolean.FALSE);
        if (StringUtil.isEmpty(equipmentId)) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令
        AgreementStructure agreement = frontHandler.buildCommand(equipmentId, "03");
        // 发送命令
        frontHandler.sendGetInfoCommand(agreement);
        return success("发送指令成功!");
    }


    /***************************************************************************
     * 下发 自检 (07)
     * @param equipmentId 设备标识
     * @param checkType 自检命令标识 (1：启动，2：关闭）
     * @return
     */
    @PostMapping("/check")
    public AjaxResult check(String equipmentId, int checkType) {
        // 检查参数
        ResultData result = new ResultData(Boolean.FALSE);
        if (StringUtil.isEmpty(equipmentId)) {
            log.error("设备标识为空，不能下发配置指令!");
            return error("设备标识为空，不能下发配置指令!");
        }

        // 创建命令，发送命令
        AgreementStructure agreement = checkHandler.buildCommand(equipmentId, checkType);
        checkHandler.sendCheckCommand(agreement);

        return success("发送指令成功!");
    }


    /***************************************************************************
     * 下发 升级文件 (09)
     * @param equipmentId 设备标识（现在使用：主机Mac地址）
     * @param equipmentType 升级类型（0-主机，1-dsp，2-前置）
     * @param fileName 要升级的文件名
     */
    @PostMapping("/sendUpgradeFile")
    public AjaxResult sendUpgradeFile(String equipmentId, Integer equipmentType, String fileName) {
        // 检查参数
        if (StringUtil.isEmpty(equipmentId)) {
            log.error("设备标识 为空，不能下发指令!");
            return error("设备标识为空，不能下发配置指令!");
        }
        if (equipmentType == null) {
            log.error("要升级的设备类型为空，不能下发指令!");
            return error("要升级的设备类型为空，不能下发指令!");
        }
        if (StringUtil.isEmpty(fileName)) {
            log.error("要升级的文件名为空，不能下发指令!");
            return error("要升级的文件名为空，不能下发指令!");
        }
        String upgradeFileType = dataCacheTool.getUpgradeFileType(equipmentType);
        if (StringUtil.isEmpty(upgradeFileType)) {
            log.error("要升级的设备类型为空，不能下发指令!");
            return error("要升级的设备类型为空，不能下发指令!");
        }

        // 得到升级文件内容
        List<String> lstUpgradeFileContent = null;
        try {
            lstUpgradeFileContent = upgradeHandler.getUpgradeFileContent(equipmentId, equipmentType, fileName);
            if (lstUpgradeFileContent == null || lstUpgradeFileContent.size() == 0) {
                log.error("读取要升级的文件内容为空，不能下发指令!");
                return error("读取要升级的文件内容为空，不能下发指令!");
            }
            log.info("---------------------------------------------------------------");
            log.info("升级文件名称：{}， 长度：{}", fileName, lstUpgradeFileContent.size());
            log.info("---------------------------------------------------------------");

            UpgradeProgress upgradeProgress = new UpgradeProgress();
            upgradeProgress.setNumber(0);
            upgradeProgress.setCount(lstUpgradeFileContent.size());
            redisService.setCacheObject(equipmentId + "-UpgradeFile-" + equipmentType,
                    upgradeProgress, dataCacheTime, TimeUnit.MINUTES);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取升级文件内容异常!");
            return error("获取升级文件内容异常!");
        }

        // 缓存升级信息
        dataCacheTool.putUpgradeFileContentCache(equipmentId + "#" + upgradeFileType, lstUpgradeFileContent);
        dataCacheTool.putUpgradeFileRateCache(equipmentId + "#" + upgradeFileType, 0);

        // 发送命令
        AgreementStructure agreement = upgradeHandler.getUpgradeAgreement(equipmentId, lstUpgradeFileContent.get(0));
        upgradeHandler.sendCommand(agreement);
        return success("发送指令成功!");
    }

    /**
     * 下发 获取上一个主机程序版本
     * @param equipmentId 设备标识（现在使用：主机Mac地址）
     */
    @PostMapping("/getPreviousVersion")
    public AjaxResult getPreviousVersion(String equipmentId) {
        // 检查参数
        if (StringUtil.isEmpty(equipmentId)) {
            log.error("设备标识 为空，不能下发指令!");
            return error("设备标识 为空，不能下发指令!");
        }

        upgradeHandler.getPreviousVersion(equipmentId);
        return success("发送指令成功!");
    }

    /**
     * 下发 主机重启
     * @param equipmentId 设备标识（现在使用：主机Mac地址）
     */
    @PostMapping("/restart")
    public AjaxResult restart(String equipmentId) {
        // 检查参数
        if (StringUtil.isEmpty(equipmentId)) {
            log.error("设备标识 为空，不能下发指令!");
            return error("设备标识 为空，不能下发指令!");
        }

        upgradeHandler.restart(equipmentId);
        return success("发送指令成功!");
    }

    /**
     * 下发 执行设备升级
     */
    @PostMapping("/equipmentUpgrade")
    public AjaxResult equipmentUpgrade(@RequestBody UpgradeEquipmentData data) {
        // 检查参数
        ResultData result = new ResultData(Boolean.FALSE);
        if (StringUtil.isEmpty(data.getEquipmentId())) {
            log.error("设备标识 为空，不能下发指令!");
            return error("设备标识 为空，不能下发指令!");
        }
        if (data.getEquipmentType() == null) {
            log.error("要升级的设备类型为空，不能下发指令!");
            return error("要升级的设备类型为空，不能下发指令!");
        }

        upgradeHandler.equipmentUpgrade(data);
        return success("发送指令成功!");
    }

    /**
     * 下发：MVB测试数据
     */
    @PostMapping("/sendMvbTestData")
    public AjaxResult sendMvbTestData(@RequestBody MvbDataCommandParam param) {
        if (param == null || ObjectUtils.isEmpty(param.getEquipmentNo())) {
            return error("主机设备编号不能为空");
        }

        mvbHandler.sendTestData(param);
        return success("发送指令成功!");
    }



    /**
     * 模拟指令：开始采集数据
     */
    @GetMapping("/test/collectionData/start")
    public AjaxResult collectionDataStart(String equipmentId) {
        // 检查参数
        if (StringUtil.isEmpty(equipmentId)) {
            log.error("设备标识 为空，不能下发指令!");
            return error("设备标识 为空，不能下发指令!");
        }

        fileHandler.collectionDataStart(equipmentId);
        return success("发送指令成功!");
    }


    /**
     * 获取：客户端连接-车序号
     * @param clientChannelId 客户端连接标识（设备ID）
     * @return String 车序号
     */
    private String getEquipmentSeq(String clientChannelId) {
        Integer equipmentSeq = nettyClientManager.getClientChannelSeq(clientChannelId);
        if (ObjectUtils.isNull(equipmentSeq)) {
            return "00";
        }
        return CodeConvertor.intToHexString(equipmentSeq, 1);
    }


}